/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2005-2009 PRO ACTIVE SAS - www.proactive-group.com
   
  Ref_Calypso.c
  -------------

  This is the reference applications that shows how to retrieve Calypso
  card's identifier.

  It performs also ISO 14443-A and ISO 14443-B polling, so that we can
  study the side-effects of switching between different configs.
  Clearly we see that going to ISO 14443-A resets the Calypso card.
  
  JDA 14/11/2005 : initial release

*/
#include "products/springprox/springprox.h"
#include "cardware/calypso/calypso_api.h"

#ifdef WIN32
#include <conio.h>              /* For kbhit and getch */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

P_CALYPSO_CTX ctx = NULL;
char *parser_output = NULL;
#define PARSER_OUTPUT_SIZE 64*1024   /* 64 ko, must be enough ! */

void get_line(char *s, DWORD n);
void exit_handler(void);



int main(int argc, char **argv)
{
  int i;

  char s_buffer[64];

  BYTE uid[12];  /* Serial number of the found card */
  BYTE uidlen;

  BYTE info[64]; /* Protocol-related information of the found card (mandatory for Innovatron protocol) */
  BYTE infolen;

  int do_start_transaction = 0;
  int no_xml_output = 0;
  
  WORD protos = PROTO_14443_A|PROTO_14443_B|PROTO_14443_Bi;
  WORD proto;
  DWORD rc = MI_OK;

  char *xml_filename = NULL;

  char *device = NULL;
  char *aid = NULL;

  atexit(exit_handler);

  printf("SpringProx (Legacy) SDK\n");
  printf("\n");
  printf("Calypso demo software (XML dump of '1TIC.ICA' card application)\n");
  printf("---------------------------------------------------------------\n");
  printf("www.springcard.com\n\n");

  for (i=1; i<argc; i++)
  {
    if (!strcmp("-d", argv[i]))
    {
      if (++i<argc)
      {
        device = argv[i];
        printf("Selecting device '%s'\n", device);
      } else
      {
        printf("Syntax error\n");
        exit(EXIT_FAILURE);
      }
    } else
    if (!strcmp("-o", argv[i]))
    {
      if (++i<argc)
      {
        xml_filename = argv[i];
        printf("Selecting output file '%s'\n", xml_filename);
      } else
      {
        printf("Syntax error\n");
        exit(EXIT_FAILURE);
      }
    } else
    if (!strcmp("-a", argv[i]))
    {
      if (++i<argc)
      {
        aid = argv[i];
        printf("Selecting AID '%s'\n", aid);
      } else
      {
        printf("Syntax error\n");
        exit(EXIT_FAILURE);
      }
    } else
    if (!strcmp("-A", argv[i]))
    {
    	protos = PROTO_14443_A;
    } else
    if (!strcmp("-B", argv[i]))
    {
    	protos = PROTO_14443_B;
    } else
    if (!strcmp("-I", argv[i]))
    {
    	protos = PROTO_14443_Bi;
    } else
    if (!strcmp("-t", argv[i]))
    {
    	do_start_transaction = 1;
    } else
    if (!strcmp("-s", argv[i]))
    {
    	no_xml_output = 1;
    } else
    if (!strcmp("-v", argv[i]))
    {
      CalypsoSetTraceLevel(255);
      CalypsoSetTraceFile("stdout");
    }
  }

  /* Allocate Calypso context and parser output */
  ctx = CalypsoCreateContext();
  parser_output = malloc(PARSER_OUTPUT_SIZE);
  if ((ctx == NULL) || (parser_output == NULL))
  {
    printf("Out of memory\n");
    goto failed;
  }

  /* Check that the library is working */
  rc = SPROX_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get SpringProx API version\n");
    goto failed;
  }
  printf("SpringProx API is '%s'\n", s_buffer);

  /* Open the SpringProx reader */
  rc = SPROX_ReaderOpen(device);
  if (rc != MI_OK)
  {
    printf("Reader not found\n");
    goto failed;
  }

  rc = SPROX_ReaderGetDevice(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("SpringProx reader found on '%s'\n", s_buffer);

  rc = SPROX_ReaderGetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("SpringProx reader is '%s'\n", s_buffer);

  printf("\n");

  /* Wait until a smartcard arrives */
#ifdef WIN32
  printf("Waiting for a contactless smartcard... (Press any key to exit)\n\n");
  while (!kbhit())
#else
  printf("Waiting for a contactless smartcard... (Press <Ctrl>+C to exit)\n\n");
  for (;;)
#endif
  {
    uidlen  = sizeof(uid);
    infolen = sizeof(info);
    rc = SPROX_FindEx(protos, &proto, uid, &uidlen, info, &infolen);
    if (rc != MI_NOTAGERR)
      break;
  }

  if (rc == MI_NOTAGERR)
  {
    printf("Cancelled...\n");
    exit(EXIT_FAILURE);
  }

  if (rc != MI_OK) 
  {
    printf("Failed to find a card\n");
    goto failed;
  }

  printf("Card found, proto=%04X, UID=", proto);
  for (i=0; i<uidlen; i++)
    printf("%02X", uid[i]);
  printf("\n");

  /* Try to bind our CALYPSO library to the card */
  rc = CalypsoCardBindLegacyEx(ctx, proto, info, infolen);
  if (rc)
  {
    printf("CalypsoCardBindLegacy failed, error %04X.\n", rc);
    exit(EXIT_FAILURE);
  }

  /* Check this card is actually CALYPSO (ATR parsing or SELECT APPLICATION + FCI parsing) */
  if (aid == NULL)
  {
    rc = CalypsoCardActivate(ctx, NULL, 0);
  } else
  {
    rc = CalypsoCardActivate(ctx, aid, strlen(aid));
  }
  if (rc)
  {
    if (rc == CALYPSO_CARD_NOT_SUPPORTED)
    {
      printf("This is not a CALYPSO card !\n");
    } else
    {
      printf("CalypsoCardActivate failed, error %04X.\n", rc);
    }
    exit(EXIT_FAILURE);
  }
  
  if (do_start_transaction)
  {
	  /* Do a dummy transaction test */  
    BYTE chal[8];
    BYTE resp[128];
    CALYPSO_SZ size = sizeof(resp);
    BYTE rev;

    rc = CalypsoCardGetChallenge(ctx, chal);
		if (rc)
		{
      printf("CalypsoCardGetChallenge failed, error %04X.\n", rc);
      exit(EXIT_FAILURE);		
		}

    rev = (BYTE) CalypsoCardRevision(ctx);

    switch (rev)
    {
      case 1  : rc = CalypsoCardOpenSecureSession1(ctx, resp, &size, CALYPSO_KEY_DEBIT, CALYPSO_SFI_ENVIRONMENT, 1, chal, chal, NULL, NULL, NULL);
                break;

      case 2  : rc = CalypsoCardOpenSecureSession2(ctx, resp, &size, CALYPSO_KEY_DEBIT, CALYPSO_SFI_ENVIRONMENT, 1, chal, chal, NULL, NULL, NULL, NULL);
                break;

      case 3  : 
      default : rc = CalypsoCardOpenSecureSession3(ctx, resp, &size, CALYPSO_KEY_DEBIT, CALYPSO_SFI_ENVIRONMENT, 1, chal, chal, NULL, NULL, NULL, NULL, NULL);
                break;
    }

		if (rc)
		{
      printf("CalypsoCardOpenSecureSession failed, error %04X.\n", rc);
      exit(EXIT_FAILURE);		
		}

    rc = CalypsoCardCloseSecureSession(ctx, FALSE, NULL, NULL, NULL);
		if (rc)
		{
      printf("CalypsoCardCloseSecureSession failed, error %04X.\n", rc);
      exit(EXIT_FAILURE);		
		}

    printf("CALYPSO pseudo-transaction OK...\n");
	}

  /* OK, let's explore the card */
  /* -------------------------- */
  printf("\nExploring the CALYPSO card...\n");

  /* Configure the parser output */
  CalypsoSetXmlOutputStr(ctx, parser_output, PARSER_OUTPUT_SIZE);

  /* Initialise the timer */
  CalypsoBench(TRUE);

  /* Actual exploration */
  rc = CalypsoExploreAndParse(ctx);
  if (rc)
  {
    printf("CalypsoExploreAndParse failed, error %04X.\n", rc);
    exit(EXIT_FAILURE);
  }

  /* Done ! */
  printf("CALYPSO card explored in %ldms\n", CalypsoBench(TRUE));

  /* Stop the parser */
  CalypsoClearOutput(ctx);

  /* Drop card connection */
  CalypsoCardDispose(ctx);

  /* Now display card's data */
  /* ----------------------- */

  if (xml_filename != NULL)
  {
    FILE *fp;

    fp = fopen(xml_filename, "wt");
    if (fp == NULL)
    {
      printf("Failed to open output file '%s'\n", xml_filename);
      exit(EXIT_FAILURE);
    }

    fprintf(fp, "<calypso_explorer>\n");

    fprintf(fp, parser_output);

    fprintf(fp, "</calypso_explorer>\n");

    fprintf(fp, "\n");
    fclose(fp);

    printf("Output saved to '%s'\n", xml_filename);
  } else
  if (!no_xml_output)
  {
    printf(parser_output);
    printf("\n");
  }

  /* Terminated, OK */
  exit(EXIT_SUCCESS);

failed:
  /* Display last error */
  if (rc != MI_OK)
  {
    printf("%s (%d)\n", SPROX_GetErrorMessage((SWORD) rc), (SWORD) rc);
  }
  exit(EXIT_FAILURE);
}


void get_line(char *s, DWORD n)
{
  DWORD w = 0;
  int c;

  memset(s, '\0', n);
  while ((c = fgetc (stdin)) != '\n' && c != EOF)
  {
    if (w < (n - 1))
    {
      s[w] = c;
      w++;
    }
  }
}

void exit_handler(void)
{
  SPROX_ReaderClose();

  if (ctx != NULL)
  {
    CalypsoDestroyContext(ctx);
    ctx = NULL;
  }

  if (parser_output != NULL)
  {
    free(parser_output);
    parser_output = NULL;
  }
}
